package com.gitee.hermer.boot.jee.generate.orm.ibatis;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.gitee.hermer.boot.jee.commons.collection.CollectionUtils;
import com.gitee.hermer.boot.jee.commons.utils.StringUtils;
import com.gitee.hermer.boot.jee.generate.properties.GenerateProperties;
import com.mysql.jdbc.ResultSetMetaData;




public class  IbatisGenerateMapper extends GenerateProperties{



	protected static String PACKAGE_TYPE = ENTITY_PACKAGE_NAME+"."+CLASS_NAME;


	public static void generate(){
		try{
			Map<String, String> dbFields = queryTableField(TABLENAME);
			
			List<String> dbIDFields =queryTablePKField(TABLENAME);
			Map<String, String> fields = new HashMap<String, String>();

			
			createSqlMapXmlFile(dbFields, fields); 
			createEntityClassFile(dbFields, fields);
			
			if(StringUtils.isNotEmpty(DAO_PACKAGE_NAME)){
				new File(PROJECT_PATH+"/main/java/"+File.separator+DAO_PACKAGE_NAME.replaceAll("\\.", "\\\\")).mkdirs();
				String path = PROJECT_PATH+"/main/java/"+File.separator+DAO_PACKAGE_NAME.replaceAll("\\.", "\\\\")+"/I"+CLASS_NAME+"Dao.java";
				StringBuffer buffer = new StringBuffer();
				appentImportBuffer(buffer, "import org.apache.ibatis.annotations.Mapper;");
				appentImportBuffer(buffer, "import com.gitee.hermer.boot.jee.orm.IBaseDao;");
				appentImportBuffer(buffer, "import "+ENTITY_PACKAGE_NAME+"."+CLASS_NAME+";");
				String c= "package {packageName};\n\n{importBuffer}\n@Mapper\npublic interface I"+CLASS_NAME+"Dao extends IBaseDao<"+CLASS_NAME+","+sqlType2JavaType(dbFields.get(dbIDFields.get(0)),buffer)+">{\n}";
				c=c.replace("{packageName}", DAO_PACKAGE_NAME);
				c=c.replace("{importBuffer}", buffer.toString());
				File file = getWriterFile(path);

				FileWriter fw = new FileWriter(file); 
				PrintWriter pw = new PrintWriter(fw);  
				pw.println(c);  
				pw.flush();  
				pw.close();  
			}
			
			createSqlMapXmlFile(dbFields, fields); 
			
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
		
		


	private static void createEntityClassFile(Map<String, String> dbFields,
			Map<String, String> fields) throws Exception {
		StringBuffer buffer = new StringBuffer();
		buffer.append("package ").append(ENTITY_PACKAGE_NAME).append(";\r\n\n{importBuffer}\n").append("public class ").append(CLASS_NAME).append(" implements Serializable {\r\n\n");

		StringBuffer importBuffer = new StringBuffer();
		appentImportBuffer(importBuffer, "import java.io.Serializable;");
		appentImportBuffer(importBuffer, "import com.gitee.hermer.boot.jee.orm.annotation.Id;");
		
		List<String> dbIDFields =queryTablePKField(TABLENAME);
		
		processAllAttrs(dbFields, fields, buffer,importBuffer,dbIDFields);
		buffer.append("\n");
		processAllMethod(dbFields, fields, buffer);

		String content = buffer.append("\r}").toString().replace("{importBuffer}", importBuffer.toString());
		//		System.out.println("\t\t\t\t"+content);
		String fileName = PROJECT_PATH+"/main/java/"+File.separator+ENTITY_PACKAGE_NAME.replaceAll("\\.", "\\\\")+File.separator+CLASS_NAME+".java";


		File file = getWriterFile(fileName);

		FileWriter fw = new FileWriter(file); 
		PrintWriter pw = new PrintWriter(fw);  
		pw.println(content);  
		pw.flush();  
		pw.close();  

	}


	private static File getWriterFile(String fileName) throws IOException {
		File file = new File(fileName);
		File p = file.getParentFile();
		if (!p.exists() || !p.isDirectory()) 
			p.mkdirs();
		if (!file.exists() || !file.isFile()) {
			p.createNewFile();
		}
		return file;
	}


	private static void processAllMethod(Map<String, String> dbFields,
			Map<String, String> fields, StringBuffer buffer) {
		for (String key : dbFields.keySet()) {
			buffer.append("\tpublic void set" + initcap(fields.get(key)) + "("  
					+ sqlType2JavaType(dbFields.get(key),null) + " " + fields.get(key)  
					+ "){\r\n");  
			buffer.append("\t\tthis." + fields.get(key) + "=" + fields.get(key) + ";\r\n");  
			buffer.append("\t}\r\n");  

			buffer.append("\tpublic " + sqlType2JavaType(dbFields.get(key),null) + " get"  
					+ initcap(fields.get(key)) + "(){\r\n");  
			buffer.append("\t\treturn " + fields.get(key) + ";\r\n");  
			buffer.append("\t}\r\n");  
		}
	}


	private static String initcap(String str) {  
		char[] ch = str.toCharArray();  
		if (ch[0] >= 'a' && ch[0] <= 'z') {  
			ch[0] = (char) (ch[0] - 32);  
		}  
		return new String(ch);  
	}  




	private static void processAllAttrs(Map<String, String> dbFields,
			Map<String, String> fields, StringBuffer buffer,StringBuffer importBuffer,List<String> dbIDFields) {
		for (String key : dbFields.keySet()) {
			if(dbIDFields.contains(key)){
				buffer.append("\t@Id\r\n");
			}
			buffer.append("\tprivate ").append(sqlType2JavaType(dbFields.get(key),importBuffer)).append(" ").append(fields.get(key)).append(";\r\n");
		}
	}

	private static String sqlType2JavaType(String sqlType,StringBuffer importBuffer) {  
		if (sqlType.equalsIgnoreCase("VARCHAR")
				|| sqlType.equalsIgnoreCase("CHAR")
				|| sqlType.equalsIgnoreCase("TEXT")) {  
			return "String";  
		}else if (sqlType.equalsIgnoreCase("BLOB")){
			return "byte[]";
		}else if (sqlType.equalsIgnoreCase("INTEGER")
				|| sqlType.equalsIgnoreCase("TINYINT")
				|| sqlType.equalsIgnoreCase("SMALLINT")
				|| sqlType.equalsIgnoreCase("MEDIUMINT")
				|| sqlType.equalsIgnoreCase("INT")){
			return "Integer";
		}else if (sqlType.equalsIgnoreCase("BIT")
				|| sqlType.equalsIgnoreCase("BOOLEAN"))
		{
			return "Boolean";
		}else if (sqlType.equalsIgnoreCase("BIGINT")){
			appentImportBuffer(importBuffer,"import java.math.BigInteger;");
			return "BigInteger";
		}else if (sqlType.equalsIgnoreCase("FLOAT")){
			return "Float";
		}else if (sqlType.equalsIgnoreCase("DOUBLE")){
			return "Double";
		}else if (sqlType.equalsIgnoreCase("DECIMAL")){
			appentImportBuffer(importBuffer,"import java.math.BigDecimal;");
			return "BigDecimal";
		}else if (sqlType.equalsIgnoreCase("DATE")
				|| sqlType.equalsIgnoreCase("YEAR")) {  	
			appentImportBuffer(importBuffer,"import java.sql.Date;");
			return "Date";
		}else if (sqlType.equalsIgnoreCase("DATETIME")
				|| sqlType.equalsIgnoreCase("TIMESTAMP")) {  	
			appentImportBuffer(importBuffer,"import java.sql.Timestamp;");
			return "Timestamp";
		}else if (sqlType.equalsIgnoreCase("TIME")) {  	
			appentImportBuffer(importBuffer,"import java.sql.Time;");
			return "Time";
		}


		return null;  
	}


	private static void appentImportBuffer(StringBuffer importBuffer,String content) {
		if (importBuffer != null && importBuffer.indexOf(content) == -1){
			importBuffer.append(content).append("\r\n");
		}
	}  

	private static void createSqlMapXmlFile(Map<String, String> dbFields,
			Map<String, String> fields) throws SQLException, Exception,
	IOException {
		String whereName = TABLENAME+"_WHERE_CLAUSE";
		String UPDATE_TAG = "update";
		String INSERT_TAG = "insert";
		String DELETE_TAG = "delete";
		String SELECT_TAG = "select";

		List<String> dbPkField = queryTablePKField(TABLENAME);
		if (dbPkField == null || CollectionUtils.isEmpty(dbPkField) || dbFields.size() <= 0) {
			throw new Exception(TABLENAME+"表结构有误！");
		}

		Document document = DocumentHelper.createDocument();
		document.addDocType("mapper", "-//mybatis.org//DTD Mapper 3.0//EN", "http://mybatis.org/dtd/mybatis-3-mapper.dtd");
		Element sqlMap = document.addElement("mapper").addAttribute("namespace", DAO_PACKAGE_NAME+".I"+CLASS_NAME+"Dao");

		Element resultMap = sqlMap.addElement("resultMap");
		resultMap.setAttributeValue("type",ENTITY_PACKAGE_NAME+'.'+CLASS_NAME);
		resultMap.setAttributeValue("id", TABLENAME);

		for (String column : dbFields.keySet()) {
			Element result = resultMap.addElement("result");
			String fieldName = replacefirstToUpper(column.toLowerCase(),"_","");
			result.setAttributeValue("property", fieldName);
			result.setAttributeValue("column",column );
			fields.put(column,fieldName );
		}

		sqlMap.addElement("cache").addAttribute("type", CACHECLASS );



		Element update = sqlMap.addElement(UPDATE_TAG);
		update.setAttributeValue("id", ENUM_BASEDAO.update.toString());
		update.setAttributeValue("parameterType", ENTITY_PACKAGE_NAME+"."+CLASS_NAME);
		update.addText("\n		UPDATE "+TABLENAME);
		Element dynamic = update.addElement("set");
		String pkField = null;
		int i = 1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			Element isNotEmpty = dynamic.addElement("if").addAttribute("test",fields.get(column)+" != null")
					.addText("\n"+appendField(column,fields.get(column),false,""));
			//
			if(i < fields.size()-1){
				i+=1;
				isNotEmpty.addText(",");
			}
			isNotEmpty.addText("\n			");

		}
		update.addText("\n		WHERE ");
		for (String pk : dbPkField) {
			update.addText(appendField(pk,pkField,false,""));
		}
		

		Element  insert = sqlMap.addElement(INSERT_TAG).addAttribute("id", ENUM_BASEDAO.insert.toString())
				.addAttribute("parameterType", ENTITY_PACKAGE_NAME+"."+CLASS_NAME).addAttribute("useGeneratedKeys", "true")
				.addAttribute("keyProperty",fields.get(dbPkField)).addText("\n		INSERT INTO ").addText(TABLENAME).addText(" (\n");
		i =1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			insert.addText("			").addText(column);
			if(i < fields.size()-1){
				i+=1;
				insert.addText(",");
			}
			insert.addText("\n");
		}
		insert.addText("		)VALUES (\n");
		i = 1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			insert.addText("			").addText("#{"+fields.get(column)+"}");
			if(i < fields.size()-1){
				i+=1;
				insert.addText(",");
			}
			insert.addText("\n");
		}
		insert.addText("		)");

		dynamic = sqlMap.addElement("sql").addAttribute("id",whereName ).addElement("where");
		i = 1;
		for (String column : fields.keySet()) {
			Element isNotEmpty = dynamic.addElement("if").addAttribute("test", fields.get(column)+" != null").addText("\n				");
			isNotEmpty.addCDATA(appendField(column,fields.get(column),true,i!=1?"AND ":"")+"				");
			isNotEmpty.addText("\n			");
			i+=1;
		}

		Element delete = sqlMap.addElement(DELETE_TAG).addAttribute("id", ENUM_BASEDAO.delete.toString())
				.addAttribute("parameterType", ENTITY_PACKAGE_NAME+"."+CLASS_NAME);
		delete.addText("\n		DELETE  FROM ").addText(TABLENAME);
		delete.addElement("include").addAttribute("refid", whereName);


		Element select = sqlMap.addElement(SELECT_TAG).addAttribute("id", ENUM_BASEDAO.list.toString()).addAttribute("resultMap", TABLENAME)
				.addAttribute("parameterType", ENTITY_PACKAGE_NAME+"."+CLASS_NAME);
		select.addText("\n		SELECT * FROM ").addText(TABLENAME).addElement("include").addAttribute("refid", whereName);
		select.addText("\n		ORDER BY ").addText(CollectionUtils.join(dbPkField, ",")).addText(" DESC");


		Element find = sqlMap.addElement(SELECT_TAG).addAttribute("id", ENUM_BASEDAO.find.toString()).addAttribute("resultMap", TABLENAME)
				.addAttribute("parameterType", ENTITY_PACKAGE_NAME+"."+CLASS_NAME)
				.addText("\n		SELECT * FROM ").addText(TABLENAME);
		find.addElement("include").addAttribute("refid", whereName);
		find.addText("\n		ORDER BY ").addText(CollectionUtils.join(dbPkField, ",")).addText(" DESC");


		Element insertBatch = sqlMap.addElement(INSERT_TAG).addAttribute("id", ENUM_BASEDAO.insertBatch.toString()).addAttribute("useGeneratedKeys", "true")
				.addAttribute("parameterType", "java.util.List")
				.addText("\n		INSERT INTO ").addText(TABLENAME).addText(" (\n");
		i = 1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			insertBatch.addText("			").addText(column);
			if(i < fields.size()-1){
				i+=1;
				insertBatch.addText(",");
			}
			insertBatch.addText("\n");
		}
		insertBatch.addText("		)VALUES ");
		Element insertForeach = insertBatch.addElement("foreach").addAttribute("collection", "list").addAttribute("item", "item").addAttribute("index", "index")
				.addAttribute("separator", ",").addText("\n			(\n");
		i = 1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			insertForeach.addText("				").addText("#{item."+fields.get(column)+"}");
			if(i < fields.size()-1){
				i+=1;
				insertForeach.addText(",");
			}
			insertForeach.addText("\n");
		}
		insertForeach.addText("			)\n		");


		Element updateBatch = sqlMap.addElement(UPDATE_TAG);
		updateBatch.setAttributeValue("id", ENUM_BASEDAO.updateBatch.toString());
		updateBatch.setAttributeValue("parameterType", "java.util.List");
		Element updateForeach = updateBatch.addElement("foreach").addAttribute("collection","list").addAttribute("item", "item")
				.addAttribute("index", "index").addAttribute("open", "").addAttribute("close", "").addAttribute("separator", ";");
		updateForeach.addText("\n		UPDATE "+TABLENAME);
		dynamic = updateForeach.addElement("set");
		i = 1;
		for (String column : fields.keySet()) {
			if (dbPkField.contains(column)) {
				pkField = fields.get(column);
				continue;
			}
			Element isNotEmpty = dynamic.addElement("if").addAttribute("test","item."+fields.get(column)+" != null")
					.addText("\n"+appendField(column,"item."+fields.get(column),false,""));
			if(i<fields.size()-1)
			{
				isNotEmpty.addText(",");
				i+=1;
			}
			isNotEmpty.addText("\n				");

		}
		updateForeach.addText("\n		WHERE ");
		
		for (String pk : dbPkField) {
			updateForeach.addText(appendField(pk,"item."+pkField,false,""));
		}



		Element deleteBatch = sqlMap.addElement(DELETE_TAG).addAttribute("id", ENUM_BASEDAO.deleteBatch.toString())
				.addAttribute("parameterType", "java.util.List");
		deleteBatch.addText("\n		DELETE  FROM ").addText(TABLENAME).addText(" WHERE ").addText(dbPkField.get(0))
		.addText(" in ");
		Element deleteForeach = deleteBatch.addElement("foreach").addAttribute("collection", "list").addAttribute("item","item")
				.addAttribute("open", "(").addAttribute("separator", ",").addAttribute("close", ")").addText("\n			 #{item.")
				.addText(pkField).addText("}\n		");


		new File(PROJECT_PATH+"/main/resources/mybatis/mapper").mkdirs();
		String fileName = PROJECT_PATH+"/main/resources/mybatis/mapper"+File.separator+TABLENAME+"_SqlMap.xml";

		//创建字符串缓冲区 
		FileWriter fileWriter = new FileWriter(getWriterFile(fileName));
		//		StringWriter stringWriter = new StringWriter();  
		//设置文件编码  
		OutputFormat xmlFormat = new OutputFormat();  
		xmlFormat.setEncoding("UTF-8"); 
		// 设置换行 
		xmlFormat.setNewlines(true); 
		// 生成缩进 
		xmlFormat.setIndent(true); 
		// 使用4个空格进行缩进, 可以兼容文本编辑器 
		xmlFormat.setIndent("    "); 

		//创建写文件方法  
		XMLWriter xmlWriter = new XMLWriter(fileWriter,xmlFormat);  
		//写入文件  
		xmlWriter.write(document);  
		// 输出xml 
		//   xmlWriter.println();
		//		System.out.println(stringWriter.toString());
		//关闭  
		xmlWriter.close();
	}

	private static String appendField(String column,String field,boolean is,String append)
	{
		StringBuffer buffer = new StringBuffer();

		buffer.append(is?"\n":"").append("					").append(append).append(column).append(" = ").append("#{").append(field).append("}").append(is?"\n":"");
		return buffer.toString();
	}

	private static List<String> queryTablePKField(String TABLENAME) throws SQLException {
		ConnectionDBUtil dbUtil = new ConnectionDBUtil();
		ResultSet  pkRSet =dbUtil.getConnection().getMetaData().getPrimaryKeys(null, null, TABLENAME);
		List<String> array = new ArrayList();
		String name = null;
		while (pkRSet.next()) {
			array.add(pkRSet.getString(4));
		}
		dbUtil.closeAll();
		return array;
	}

	private static Map<String, String> queryTableField(String TABLENAME) throws SQLException {
		Map<String, String> dbFields = new HashMap<String, String>();
		ConnectionDBUtil dbUtil = new ConnectionDBUtil();
		ResultSet resultSet = dbUtil.executeQueryRS("SELECT * FROM "+TABLENAME+" WHERE 1=0", null);
		ResultSetMetaData rsmd =(ResultSetMetaData) resultSet.getMetaData();
		for (int i = 0; i < rsmd.getColumnCount(); i++) { dbFields.put(rsmd.getColumnName(i+1), rsmd.getColumnTypeName(i+1)); }
		dbUtil.closeAll();
		return dbFields;
	}

	/** 
	 * 首字母大写 
	 *  
	 * @param srcStr 
	 * @return 
	 */  
	public static String firstCharacterToUpper(String srcStr) {  
		return srcStr.substring(0, 1).toUpperCase() + srcStr.substring(1);  
	}  
	/** 
	 * 替换字符串并让它的下一个字母为大写 
	 * @param srcStr 
	 * @param org 
	 * @param ob 
	 * @return 
	 */  
	public static String replacefirstToUpper(String srcStr,String org,String ob)  
	{  
		StringBuffer buffer = new StringBuffer();
		//		String newString = "";  
		int first=0;  
		while(srcStr.indexOf(org)!=-1)  
		{  
			first=srcStr.indexOf(org);  
			if(first!=srcStr.length())  
			{  
				buffer.append(srcStr.substring(0,first)+ob);
				srcStr=srcStr.substring(first+org.length(),srcStr.length());  
				srcStr=firstCharacterToUpper(srcStr);  
			}  
		}  
		buffer.append(srcStr);  
		return buffer.toString();  
	} 


}
